package com.nulldev.util.java.swing.canvas;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.math.BigInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JPanel;

import com.nulldev.util.java.utils.color.ColorUtil;
import com.nulldev.util.threading.v3.RunnableWorker;
import com.nulldev.util.threading.v3.ThreadDispatcher;

public abstract class SmoothCanvas extends JPanel{

	//protected final ExecutorService executor = Executors.newCachedThreadPool();
	public long FRAME_RATE = (1000L / 144L);
	public boolean vSync = true;
	public boolean aa = false;
	private BigInteger fps = new BigInteger("0");
	private PaintMethod method = PaintMethod.DEFAULT;
	
	private boolean stop = false;

	public SmoothCanvas() {
		super();
		setOpaque(true);
		setDoubleBuffered(true);
		setIgnoreRepaint(true);
		ThreadDispatcher.instance().addTask(RunnableWorker.fromRunnable(new Runnable() {

			@Override
			public void run() {
				while (true) {
					if (SmoothCanvas.this.stop) {
						break;
					}
					try {
						if (getBackground() == ColorUtil.TRANSPARENT) {
							setBackground(ColorUtil.TRANSPARENT);
						}
						
						if (method == PaintMethod.DEFAULT) {
							paint(getGraphics());
						}else if (method == PaintMethod.PAINT_IMM) {
							paintImmediately(getBounds());
						}else if (method == PaintMethod.REPAINT) {
							repaint();
						}else if (method == PaintMethod.REPAINT_DELAYED){
							repaint(FRAME_RATE * 1000);
						}
						if (vSync) {
							Toolkit.getDefaultToolkit().sync();
						}
						if (FRAME_RATE > 0) {
							try {
								Thread.sleep(FRAME_RATE);
							} catch (Exception e) {
							}
						}
					}catch (Exception e) {
					}
				}
			}
		}, "$SmoothCanvas.worker"));
	}

	@Override
	public void paintComponent(final Graphics g) {
		super.paintComponent(g);
		this.fps = fps.add(new BigInteger("1"));
		if (this.aa) {
			((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		}
		this.drawCanvas((Graphics2D) g);
	}
	
	public BigInteger getFPS() {
		return this.fps;
	}

	public boolean isVSyncEnabled() {
		return this.vSync;
	}
	
	public boolean isAAEnabled() {
		return this.aa;
	}
	
	public void setAAEnabled(final boolean state) {
		this.aa = state;
	}
	
	public void setVSyncEnabled(final boolean state) {
		this.vSync = state;
	}
	
	public abstract void drawCanvas(final Graphics2D g2d);

	public void setFPS(final long FPS) {
		if (FPS > 0L) {
			this.FRAME_RATE = (1000L / (FPS + 6));			
		}else {
			this.FRAME_RATE = 0;
		}
	}

	public void usePaintMethod(final PaintMethod method) {
		this.method = method;
	}
	
	private Color cb = Color.WHITE;
	
	@Override
	public void setBackground(final Color color) {
		super.setBackground(color);
		this.cb = color;
	}
	
	public void dispose() {
		this.stop = true;
	}
}
